﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace XNET.Extension
{
    /// <summary>
    /// 反射扩展
    /// </summary>
    public static class ReflectionExtensions
    {

        /// <summary>
        /// 获取项目的属性
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="Provider">项目</param>
        /// <param name="Inherit">当为 true 时，查找继承的自定义属性的层次结构链</param>
        /// <returns></returns>
        public static T Attribute<T>(this ICustomAttributeProvider Provider, bool Inherit = true)
            where T : Attribute
        {
            return Provider.IsDefined(typeof(T), Inherit) ? Provider.Attributes<T>(Inherit)[0] : default(T);
        }

        /// <summary>
        /// 获取项目的属性
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="Provider">项目</param>
        /// <param name="Inherit">当为 true 时，查找继承的自定义属性的层次结构链</param>
        /// <returns></returns>
        public static T[] Attributes<T>(this ICustomAttributeProvider Provider, bool Inherit = true)
            where T : Attribute
        {
            return Provider.IsDefined(typeof(T), Inherit) ? Provider.GetCustomAttributes(typeof(T), Inherit).ToArray(x => (T)x) : new T[0];
        }

        /// <summary>
        /// 创建该类型的实例并给它指定的类型
        /// </summary>
        /// <typeparam name="ClassType">返回类型</typeparam>
        /// <param name="Type">创建实例</param>
        /// <param name="args">构造函数参数</param>
        /// <returns></returns>
        public static ClassType Create<ClassType>(this Type Type, params object[] args)
        {
            return (ClassType)Type.Create(args);
        }

        /// <summary>
        /// 创建该类型的实例
        /// </summary>
        /// <param name="Type">返回类型</param>
        /// <param name="args">构造函数参数</param>
        /// <returns></returns>
        public static object Create(this Type Type, params object[] args)
        {
            return Activator.CreateInstance(Type, args);
        }

        /// <summary>
        /// 创建该类型的实例并给它指定的类型
        /// </summary>
        /// <typeparam name="ClassType">返回类型</typeparam>
        /// <param name="Types">创建实例</param>
        /// <param name="args">构造函数参数</param>
        /// <returns></returns>
        public static IEnumerable<ClassType> Create<ClassType>(this IEnumerable<Type> Types, params object[] args)
        {
            return Types.ForEach(x => x.Create<ClassType>(args));
        }

        /// <summary>
        /// 创建指定类型的一个实例
        /// </summary>
        /// <param name="Types">创建实例</param>
        /// <param name="args">构造函数参数</param>
        /// <returns></returns>
        public static IEnumerable<object> Create(this IEnumerable<Type> Types, params object[] args)
        {
            return Types.ForEach(x => x.Create(args));
        }

        /// <summary>
        /// 返回类型名字
        /// </summary>
        /// <param name="ObjectType">获取类型名称</param>
        /// <returns></returns>
        public static string GetName(this Type ObjectType)
        {
            var Output = new StringBuilder();
            if (ObjectType.Name == "Void")
            {
                Output.Append("void");
            }
            else
            {
                if (ObjectType.Name.Contains("`"))
                {
                    var GenericTypes = ObjectType.GetGenericArguments();
                    Output.Append(ObjectType.Name.Remove(ObjectType.Name.IndexOf("`", StringComparison.InvariantCulture)))
                        .Append("<");
                    var Seperator = string.Empty;
                    foreach (Type GenericType in GenericTypes)
                    {
                        Output.Append(Seperator).Append(GenericType.GetName());
                        Seperator = ",";
                    }
                    Output.Append(">");
                }
                else
                {
                    Output.Append(ObjectType.Name);
                }
            }
            return Output.ToString();
        }

        /// <summary>
        /// 确定类型的默认构造函数
        /// </summary>
        /// <param name="Type">检查类型</param>
        /// <returns></returns>
        public static bool HasDefaultConstructor(this Type Type)
        {
            return Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
                        .Any(x => x.GetParameters().Length == 0);
        }

        /// <summary>
        /// 确定类型的默认构造函数
        /// </summary>
        /// <param name="ObjectType">对象类型</param>
        /// <param name="Type">类型</param>
        /// <returns></returns>
        public static bool Is(this Type ObjectType, Type Type)
        {
            if (ObjectType == null)
            {
                return false;
            }
            if (Type == ObjectType || ObjectType.GetInterfaces().Any(x => x == Type))
            {
                return true;
            }
            if (ObjectType.BaseType == null)
            {
                return false;
            }
            return ObjectType.BaseType.Is(Type);
        }

        /// <summary>
        /// Determines if an object is of a specific type
        /// </summary>
        /// <param name="ObjectType">Object type</param>
        /// <typeparam name="BaseObjectType">Base object type</typeparam>
        /// <returns>True if it is, false otherwise</returns>
        public static bool Is<BaseObjectType>(this Type ObjectType)
        {
            return ObjectType.Is(typeof(BaseObjectType));
        }

        /// <summary>
        /// 加载程序集的名称
        /// </summary>
        /// <param name="Name">程序集名称</param>
        /// <returns></returns>
        public static System.Reflection.Assembly Load(this AssemblyName Name)
        {
            try
            {
                return AppDomain.CurrentDomain.Load(Name);
            }
            catch (BadImageFormatException)
            {
                return null;
            }
        }

        /// <summary>
        /// 加载目录里面程序集，返回程序集数组
        /// </summary>
        /// <param name="Directory">目录</param>
        /// <param name="Recursive">是否递归</param>
        /// <returns></returns>
        public static IEnumerable<Assembly> LoadAssemblies(this DirectoryInfo Directory, bool Recursive = false)
        {
            var Assemblies = new List<Assembly>();
            foreach (FileInfo File in Directory.GetFiles("*.dll", Recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly))
            {
                try
                {
                    Assemblies.Add(AssemblyName.GetAssemblyName(File.FullName).Load());
                }
                catch (BadImageFormatException)
                {
                }
            }
            return Assemblies;
        }

        /// <summary>
        /// 查找一个类型列表并确定它们是否有一个特定的属性
        /// </summary>
        /// <typeparam name="T">属性类型</typeparam>
        /// <param name="Types">要检查的类型</param>
        /// <param name="Inherit">当为 true 时，查找继承的自定义属性的层次结构链</param>
        /// <returns></returns>
        public static IEnumerable<Type> MarkedWith<T>(this IEnumerable<Type> Types, bool Inherit = true)
            where T : Attribute
        {
            if (Types == null)
            {
                return null;
            }
            return Types.Where(x => x.IsDefined(typeof(T), Inherit) && !x.IsAbstract);
        }

        /// <summary>
        /// 对象浅拷贝
        /// </summary>
        /// <param name="Object">要拷贝的对象</param>
        /// <param name="SimpleTypesOnly">如果true,只拷贝类型 , false 拷贝所有.</param>
        /// <returns></returns>
        public static T MakeShallowCopy<T>(this T Object, bool SimpleTypesOnly = false)
        {
            if (Object == null)
            {
                return default(T);
            }
            var ObjectType = Object.GetType();
            var ClassInstance = ObjectType.Create<T>();
            foreach (PropertyInfo Property in ObjectType.GetProperties())
            {
                try
                {
                    if (Property.CanRead
                            && Property.CanWrite
                            && SimpleTypesOnly
                            && Property.PropertyType.IsValueType)
                    {
                        Property.SetValue(ClassInstance, Property.GetValue(Object, null), null);
                    }
                    else
                    {
                        if (!SimpleTypesOnly
                                && Property.CanRead
                                && Property.CanWrite)
                        {
                            Property.SetValue(ClassInstance, Property.GetValue(Object, null), null);
                        }
                    }
                }
                catch
                {
                }
            }

            foreach (FieldInfo Field in ObjectType.GetFields())
            {
                try
                {
                    if (SimpleTypesOnly && Field.IsPublic)
                    {
                        Field.SetValue(ClassInstance, Field.GetValue(Object));
                    }
                    else
                    {
                        if (!SimpleTypesOnly && Field.IsPublic)
                        {
                            Field.SetValue(ClassInstance, Field.GetValue(Object));
                        }
                    }
                }
                catch
                {
                }
            }

            return ClassInstance;
        }

        /// <summary>
        /// 返回指定基类或者接口的所有实例
        /// </summary>
        /// <typeparam name="ClassType">基类或接口</typeparam>
        /// <param name="Assembly">程序集</param>
        /// <returns></returns>
        public static IEnumerable<ClassType> Objects<ClassType>(this Assembly Assembly)
        {
            return Assembly.Types<ClassType>().Where(x => !x.ContainsGenericParameters).Create<ClassType>();
        }

        /// <summary>
        /// 返回指定一组基类或者接口的所有实例
        /// </summary>
        /// <typeparam name="ClassType">基类或接口</typeparam>
        /// <param name="Assemblies">程序集</param>
        /// <returns></returns>
        public static IEnumerable<ClassType> Objects<ClassType>(this IEnumerable<Assembly> Assemblies)
        {
            var ReturnValues = new List<ClassType>();
            foreach (Assembly Assembly in Assemblies.Where(x => x != null))
            {
                ReturnValues.AddRange(Assembly.Objects<ClassType>());
            }
            return ReturnValues;
        }

        /// <summary>
        /// 返回指定目录基类或者接口的所有实例
        /// </summary>
        /// <typeparam name="ClassType">基类或接口</typeparam>
        /// <param name="Directory">目录</param>
        /// <param name="Recursive">是否递归</param>
        /// <returns></returns>
        public static IEnumerable<ClassType> Objects<ClassType>(this DirectoryInfo Directory, bool Recursive = false)
        {
            return Directory.LoadAssemblies(Recursive).Objects<ClassType>();
        }

        /// <summary>
        /// 获取一个调用指定属性设置函数的lambda表达式
        /// </summary>
        /// <typeparam name="ClassType">类型</typeparam>
        /// <typeparam name="DataType">数据类型</typeparam>
        /// <param name="Property">属性</param>
        /// <returns></returns>
        public static Expression<Func<ClassType, DataType>> PropertyGetter<ClassType, DataType>(this PropertyInfo Property)
        {
            if (!Property.PropertyType.Is(typeof(DataType)))
            {
                throw new ArgumentException("Property is not of the type specified");
            }
            if (!Property.DeclaringType.Is(typeof(ClassType)) && !typeof(ClassType).Is(Property.DeclaringType))
            {
                throw new ArgumentException("Property is not from the declaring class type specified");
            }
            var ObjectInstance = Expression.Parameter(Property.DeclaringType, "x");
            var PropertyGet = Expression.Property(ObjectInstance, Property);
            if (Property.PropertyType != typeof(DataType))
            {
                var Convert = Expression.Convert(PropertyGet, typeof(DataType));
                return Expression.Lambda<Func<ClassType, DataType>>(Convert, ObjectInstance);
            }
            return Expression.Lambda<Func<ClassType, DataType>>(PropertyGet, ObjectInstance);
        }

        /// <summary>
        /// 获取一个调用指定属性设置函数的lambda表达式
        /// </summary>
        /// <typeparam name="ClassType">类型</typeparam>
        /// <param name="Property">属性</param>
        /// <returns></returns>
        public static Expression<Func<ClassType, object>> PropertyGetter<ClassType>(this PropertyInfo Property)
        {
            return Property.PropertyGetter<ClassType, object>();
        }

        /// <summary>
        /// 获取属性名称
        /// </summary>
        /// <param name="Expression">LINQ表达式</param>
        /// <returns></returns>
        public static string PropertyName(this LambdaExpression Expression)
        {
            if (Expression.Body is UnaryExpression && Expression.Body.NodeType == ExpressionType.Convert)
            {
                var Temp = (MemberExpression)((UnaryExpression)Expression.Body).Operand;
                return Temp.Expression.PropertyName() + Temp.Member.Name;
            }
            if (!(Expression.Body is MemberExpression))
            {
                throw new ArgumentException("Expression.Body is not a MemberExpression");
            }
            return ((MemberExpression)Expression.Body).Expression.PropertyName() + ((MemberExpression)Expression.Body).Member.Name;
        }

        /// <summary>
        /// 获取属性名称
        /// </summary>
        /// <param name="Expression">LINQ 表达式</param>
        /// <returns></returns>
        public static string PropertyName(this Expression Expression)
        {
            var TempExpression = Expression as MemberExpression;
            if (TempExpression == null)
            {
                return string.Empty;
            }
            return TempExpression.Expression.PropertyName() + TempExpression.Member.Name + ".";
        }

        /// <summary>
        /// 获取一个调用指定属性设置函数的lambda表达式
        /// </summary>
        /// <typeparam name="ClassType">类型</typeparam>
        /// <typeparam name="DataType">数据类型</typeparam>
        /// <param name="Property">属性</param>
        /// <returns></returns>
        public static Expression<Action<ClassType, DataType>> PropertySetter<ClassType, DataType>(this Expression<Func<ClassType, DataType>> Property)
        {
            var PropertyName = Property.PropertyName();
            var SplitName = PropertyName.Split(new string[] { "." }, StringSplitOptions.RemoveEmptyEntries);
            var PropertyInfo = typeof(ClassType).GetProperty(SplitName[0]);
            var ObjectInstance = Expression.Parameter(PropertyInfo.DeclaringType, "x");
            var PropertySet = Expression.Parameter(typeof(DataType), "y");
            MethodCallExpression SetterCall = null;
            MemberExpression PropertyGet = null;
            if (SplitName.Length > 1)
            {
                PropertyGet = Expression.Property(ObjectInstance, PropertyInfo);
                for (var x = 1; x < SplitName.Length - 1; ++x)
                {
                    PropertyInfo = PropertyInfo.PropertyType.GetProperty(SplitName[x]);
                    PropertyGet = Expression.Property(PropertyGet, PropertyInfo);
                }
                PropertyInfo = PropertyInfo.PropertyType.GetProperty(SplitName[SplitName.Length - 1]);
            }
            if (PropertyInfo.PropertyType != typeof(DataType))
            {
                var Convert = Expression.Convert(PropertySet, PropertyInfo.PropertyType);
                if (PropertyGet == null)
                {
                    SetterCall = Expression.Call(ObjectInstance, PropertyInfo.GetSetMethod(), Convert);
                }
                else
                {
                    SetterCall = Expression.Call(PropertyGet, PropertyInfo.GetSetMethod(), Convert);
                }
                return Expression.Lambda<Action<ClassType, DataType>>(SetterCall, ObjectInstance, PropertySet);
            }
            if (PropertyGet == null)
            {
                SetterCall = Expression.Call(ObjectInstance, PropertyInfo.GetSetMethod(), PropertySet);
            }
            else
            {
                SetterCall = Expression.Call(PropertyGet, PropertyInfo.GetSetMethod(), PropertySet);
            }
            return Expression.Lambda<Action<ClassType, DataType>>(SetterCall, ObjectInstance, PropertySet);
        }

        /// <summary>
        /// 获取一个调用指定属性设置函数的lambda表达式
        /// </summary>
        /// <typeparam name="ClassType">类型</typeparam>
        /// <param name="Property">属性</param>
        /// <returns></returns>
        public static Expression<Action<ClassType, object>> PropertySetter<ClassType>(this Expression<Func<ClassType, object>> Property)
        {
            return Property.PropertySetter<ClassType, object>();
        }

        /// <summary>
        /// 获取属性类型
        /// </summary>
        /// <param name="ObjectType">对象类型</param>
        /// <param name="PropertyPath">属性</param>
        /// <returns></returns>
        public static Type PropertyType(this Type ObjectType, string PropertyPath)
        {
            if (ObjectType == null || string.IsNullOrEmpty(PropertyPath))
            {
                return null;
            }
            var SourceProperties = PropertyPath.Split(new string[] { "." }, StringSplitOptions.None);
            PropertyInfo PropertyInfo = null;
            for (var x = 0; x < SourceProperties.Length; ++x)
            {
                PropertyInfo = ObjectType.GetProperty(SourceProperties[x]);
                ObjectType = PropertyInfo.PropertyType;
            }
            return ObjectType;
        }

        /// <summary>
        /// 获取程序集版本信息
        /// </summary>
        /// <param name="Assembly">程序集</param>
        /// <param name="InfoType">版本</param>
        /// <returns></returns>
        public static string ToString(this Assembly Assembly, VersionInfo InfoType)
        {
            if (InfoType.HasFlag(VersionInfo.ShortVersion))
            {
                var Version = Assembly.GetName().Version;
                return Version.Major + "." + Version.Minor;
            }
            else
            {
                return Assembly.GetName().Version.ToString();
            }
        }

        /// <summary>
        /// 获取所有程序集版本信息
        /// </summary>
        /// <param name="Assemblies">程序集</param>
        /// <param name="InfoType">版本信息</param>
        /// <returns></returns>
        public static string ToString(this IEnumerable<Assembly> Assemblies, VersionInfo InfoType)
        {
            var Builder = new StringBuilder();
            Assemblies.Where(x => x != null).OrderBy(x => x.FullName).ForEach<Assembly>(x => Builder.AppendLine(x.GetName().Name + ": " + x.ToString(InfoType)));
            return Builder.ToString();
        }

        /// <summary>
        /// 获取当前所有加载程序集信息
        /// </summary>
        /// <param name="Assemblies">程序集</param>
        /// <param name="HTMLOutput">是否HTML格式输出</param>
        /// <returns></returns>
        public static string ToString(this IEnumerable<Assembly> Assemblies, bool HTMLOutput)
        {
            var Builder = new StringBuilder();
            Builder.Append(HTMLOutput ? "<strong>程序集信息</strong><br />" : "程序集信息\r\n");
            Assemblies.Where(x => x != null).ForEach<Assembly>(x => Builder.Append(x.ToString(HTMLOutput)));
            return Builder.ToString();
        }

        /// <summary>
        /// 格式化对象属性名称及属性值(使用静态类)
        /// </summary>
        /// <param name="ObjectType">对象</param>
        /// <param name="HTMLOutput">是否输出HTML格式</param>
        /// <returns></returns>
        public static string ToString(this Type ObjectType, bool HTMLOutput)
        {
            var TempValue = new StringBuilder();
            TempValue.Append(HTMLOutput ? "<table><thead><tr><th>Property Name</th><th>Property Value</th></tr></thead><tbody>" : "Property Name\t\t\t\tProperty Value");
            var Properties = ObjectType.GetProperties();
            foreach (PropertyInfo Property in Properties)
            {
                TempValue.Append(HTMLOutput ? "<tr><td>" : string.Empty).Append(Property.Name).Append(HTMLOutput ? "</td><td>" : "\t\t\t\t");
                if (Property.GetIndexParameters().Length == 0)
                {
                    try
                    {
                        TempValue.Append(Property.GetValue(null, null) == null ? "null" : Property.GetValue(null, null).ToString());
                    }
                    catch
                    {
                    }
                }
                TempValue.Append(HTMLOutput ? "</td></tr>" : string.Empty);
            }
            TempValue.Append(HTMLOutput ? "</tbody></table>" : string.Empty);
            return TempValue.ToString();
        }

        /// <summary>
        /// 获取一个基于接口类型的列表
        /// </summary>
        /// <param name="Assembly">程序集</param>
        /// <typeparam name="BaseType">类型</typeparam>
        /// <returns></returns>
        public static IEnumerable<Type> Types<BaseType>(this Assembly Assembly)
        {
            return Assembly.Types(typeof(BaseType));
        }

        /// <summary>
        /// 获取一个基于接口类型的列表
        /// </summary>
        /// <param name="Assembly">程序集</param>
        /// <param name="BaseType">类型</param>
        /// <returns></returns>
        public static IEnumerable<Type> Types(this Assembly Assembly, Type BaseType)
        {
            try
            {
                return Assembly.GetTypes().Where(x => x.Is(BaseType) && x.IsClass && !x.IsAbstract);
            }
            catch
            {
                return new List<Type>();
            }
        }

        /// <summary>
        /// 获取一个基于接口类型的列表
        /// </summary>
        /// <param name="Assemblies">程序集</param>
        /// <typeparam name="BaseType">类型</typeparam>
        /// <returns></returns>
        public static IEnumerable<Type> Types<BaseType>(this IEnumerable<Assembly> Assemblies)
        {
            return Assemblies.Types(typeof(BaseType));
        }

        /// <summary>
        /// 获取一个基于接口类型的列表
        /// </summary>
        /// <param name="Assemblies">程序集</param>
        /// <param name="BaseType">类型</param>
        /// <returns></returns>
        public static IEnumerable<Type> Types(this IEnumerable<Assembly> Assemblies, Type BaseType)
        {
            var ReturnValues = new List<Type>();
            Assemblies.Where(x => x != null).ForEach(y => ReturnValues.AddRange(y.Types(BaseType)));
            return ReturnValues;
        }

        /// <summary>
        /// Gets a list of types in the assemblies specified
        /// </summary>
        /// <param name="Assemblies">Assemblies to check</param>
        /// <returns>List of types</returns>
        public static IEnumerable<Type> Types(this IEnumerable<Assembly> Assemblies)
        {
            var ReturnValues = new List<Type>();
            Assemblies.ForEach(y =>
            {
                try
                {
                    ReturnValues.AddRange(y.GetTypes());
                }
                catch (ReflectionTypeLoadException) { }
            });
            return ReturnValues;
        }
    }

    /// <summary>
    /// 版本信息
    /// </summary>
    public enum VersionInfo
    {
        /// <summary>
        /// 长版本号
        /// </summary>
        LongVersion = 2,
        /// <summary>
        /// 短版本号
        /// </summary>
        ShortVersion = 1
    }
}
